#include <string>
#include <iostream>
#include <iomanip>
#include <ostream>

#include "Library_t.h"
#include "Book_t.h"
#include "Borrower_t.h"
using namespace std;

Library_t::Library_t(){
}

Library_t::Library_t(const Library_t& lib){
	this->books.insert(lib.books.begin(),lib.books.end());
	this->borrowers.insert(lib.borrowers.begin(),lib.borrowers.end());	
}

Library_t& Library_t::operator =(const Library_t& lib){
	if(this != &lib){
		this->books.clear();
		this->borrowers.clear();
		this->books.insert(lib.books.begin(),lib.books.end());
		this->borrowers.insert(lib.borrowers.begin(),lib.borrowers.end());
	}
	return *this;
}

Library_t::~Library_t(){
	map<unsigned long,const Borrower_t*>::const_iterator borrower_it;
	for(borrower_it=borrowers.begin(); borrower_it != borrowers.end();++borrower_it){
		delete(borrower_it->second);
	}
	map<std::string,const Book_t*>::const_iterator book_it;	
	for(book_it=books.begin();book_it != books.end();++book_it){
		delete(book_it->second); 
	}
}

bool Library_t::AddBook(std::string ISBN,const Book_t *book){
	if(book == 0 || ISBN.empty()){
		return false;
	}	
	if(books.find(ISBN) == books.end()){
		books[ISBN] = book;
		return true;
	}
	return false;
}

bool Library_t::AddBorrower(unsigned long id, const Borrower_t* borrower){
	if(borrower == 0) 
		return false;
	if(borrowers.find(id) == borrowers.end()){
		borrowers[id] = borrower;
		return true;
	}
	return false;
}

Book_t* Library_t::RemoveBook(std::string ISBN) throw (LibraryException){
	Book_t *book = SearchBook(ISBN);
	if(book != 0){
		if(book->isBorrowed()) 
			throw BOOK_REMOVE_FAIL;
		books.erase(ISBN);
	}
	return book;
}

Book_t* Library_t::SearchBook(std::string ISBN) const{
	Book_t *book = 0;
	map<string,const Book_t*>::const_iterator it = books.find(ISBN);
	if(it != books.end()){
		book = (Book_t*)it->second;
	}
	return book;
}

BookStatus Library_t::Status(std::string ISBN)const{
	BookStatus bookStatus = BOOK_UKNOWN;
	map<string,const Book_t*>::const_iterator it = books.find(ISBN);
	if(it != books.end()){
		if(it->second->available()){
			bookStatus = BOOK_AVAILABLE;
		}else{
			bookStatus = BOOK_OUT;
		}
	}
	return bookStatus;
}

bool Library_t::Add(unsigned long ID,Borrower_t *borrower){
	if(borrower == 0){
		return false;
	}
	if(borrowers.find(ID) == borrowers.end()){
		borrowers[ID] = borrower;
	}else{
		return false;
	}
	return true;
}

Borrower_t* Library_t::RemoveBorrower(unsigned long ID){
	Borrower_t *borrower = SearchBorrower(ID);
	if(borrower != 0){
		if(borrower->hasBooks())
			throw BORROWER_REMOVE_FAIL;
		borrowers.erase(ID);
		return borrower;
	}
	return 0;
}

Borrower_t* Library_t::SearchBorrower(unsigned long ID) const{
	map<unsigned long,const Borrower_t*>::const_iterator it = borrowers.find(ID);
	if(it != borrowers.end()){
		return (Borrower_t*)it->second;
	}
	return 0;
}

Book_t* Library_t::Borrow(std::string ISBN, unsigned long ID){
	Book_t *book = SearchBook(ISBN);
	if(book != 0){
		Borrower_t *borrower = SearchBorrower(ID);
		if(borrower != 0){
			if(book->borrow(borrower)){
				borrower->writeBorrowed(book);
				return book;
			}
		}
	}
	return 0;
}
bool Library_t::Return(std::string ISBN, unsigned long ID){
	Book_t *book = SearchBook(ISBN);
	if(book != 0){
		Borrower_t *borrower = SearchBorrower(ID);
		if(borrower != 0){
			if(book->bringBack(borrower)){
				borrower->removeBorrowed(book);
				return true;
			}
		}
	}
	return false;
}

void Library_t::BooksReport() const{
	map<string,const Book_t*>::const_iterator it;
	cout << "Books Report" << endl << "------------" << endl;
	int count = 1;
	for(it=books.begin();it!=books.end();++it){
		cout << "(" << count << ") " << *(it->second) << endl;
		++count;
	}
}

void Library_t::BorrowesReport() const{
	map<unsigned long,const Borrower_t*>::const_iterator it;
	cout << "Borrowers Report" << endl << "---------------" << endl;
	int count = 1;
	for(it=borrowers.begin();it!=borrowers.end();++it){
		cout << "(" << count << ") " << *(it->second);
		++count;
	}
}